﻿////////////////////////////////////////////////////////////////////////////////////////////////
//
// Copyright © Yaroslavov Alexander 2010
//
// Contacts:
// Phone: +7(906)827-27-51
// Email: x-ronos@yandex.ru
//
/////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Moppet.Lapa
{
	/// <summary>
	/// Парсер который возвращает множество вариантов разбора, т.е. обобщённый парсер
	/// Func&lt;LpText, IEnumerable&lt;LpNode&gt;&gt;.
	/// На входе текст. На выходе множество вариантов разбора, где каждый из вариантов
	/// - это пара { Часть разобранного текста; Остаток }.
	/// Например, для строки "1234" варианты разбора могут быть "", "1", "12", "123", "1234". 
	/// </summary>
	public sealed class LpmParser : LpBaseParser<IEnumerable<LpNode>, LpmParser>
	{
		/// <summary>
		/// Конструктор по умолчанию.
		/// </summary>
		public LpmParser()
		{
		}

		/// <summary>
		/// Вспомогательный конструктор.
		/// </summary>
		/// <param name="id">Идентификатор.</param>
		public LpmParser(string id) { m_identifier = id; }

		/// <summary>
		/// Вспомогательный конструктор.
		/// </summary>
		/// <param name="parser">Фактическая функция разбора - парсер.</param>
		public LpmParser(Func<LpText, IEnumerable<LpNode>> parser) { m_parser = parser; }

		/// <summary>
		/// Вспомогательный конструктор.
		/// </summary>
		/// <param name="id">Идентификатор.</param>
		/// <param name="parser">Фактическая функция разбора - парсер.</param>
		public LpmParser(string id, Func<LpText, IEnumerable<LpNode>> parser)
		{
			m_identifier = id;
			m_parser = parser;
		}

		/// <summary>
		/// Функция - парсер верхнего уровня.
		/// </summary>
		/// <param name="text">Блок текста.</param>
		/// <returns>Результаты разбора.</returns>
		public IEnumerable<LpNode> Do(LpText text)
		{
			IEnumerable<LpNode> res;
			if (m_stack != null)
			{
                //if (m_stack.FindLast(text) != null)
                //    return new LpNode[0];
                //var added = m_stack.AddLast(text);
                //res = m_parser(text);
                //m_stack.Remove(added);

                LinkedListNode<LpText> added = null;
                lock (m_stack)
                {
                    if (m_stack.FindLast(text) != null)
                        return new LpNode[0];
                    added = m_stack.AddLast(text);
                }
                res = m_parser(text);
                lock (m_stack)
                {
                    m_stack.Remove(added);
                }
			}
			else
			{
				res = m_parser(text);
			}
			if (m_identifier != null)
			{
				if (m_wrapNode)
					return WrapIdentifiers(res);
				return SetIdentifiers(res);
			}
			return res; 
		}


		/// <summary>
		/// Оборачивает результаты разбора.
		/// </summary>
		/// <param name="res">Результаты разбора.</param>
		/// <returns>Результаты.</returns>
		private IEnumerable<LpNode> WrapIdentifiers(IEnumerable<LpNode> res)
		{
			foreach (var n in res)
			{
				if (n.Id != null)
					yield return new LpNode(m_identifier, n.Match, n.Rest, n);
				n.Id = m_identifier;
				yield return n;
			}
		}

		/// <summary>
		/// Устанавливает (перезаписывает) идентификатор на результаты разбора.
		/// </summary>
		/// <param name="res">Результаты разбора.</param>
		/// <returns>Результаты.</returns>
		private IEnumerable<LpNode> SetIdentifiers(IEnumerable<LpNode> res)
		{
			foreach (var n in res)
			{
				n.Id = m_identifier;
				yield return n;
			}
		}


		/// <summary>
		/// Оператор неявного приведения типа.
		/// </summary>
		/// <param name="p">Парсер.</param>
		/// <returns>Объектный парсер.</returns>
		public static implicit operator LpmParser(Func<LpText, IEnumerable<LpNode>> p)
		{
			return new LpmParser(p);
		}

		/// <summary>
		/// Оператор неявного приведения типа.
		/// </summary>
		/// <param name="oParser">Объектный парсер.</param>
		/// <returns>Объектный парсер.</returns>
		public static implicit operator Func<LpText, IEnumerable<LpNode>>(LpmParser oParser)
		{
			if (oParser.Identifier == null && oParser.Parser != null)
				return oParser.Parser;
			return oParser.Do;
		}

		#region And

		/// <summary>
		/// Ф-я последовательного применения парсеров.
		/// </summary>
		/// <param name="left">Левый парсер.</param>
		/// <param name="right">Правый парсер.</param>
		/// <param name="p">Анализируемый текст.</param>
		/// <returns>Результаты разбора.</returns>
		private static IEnumerable<LpNode> And(LpmParser left, LpsParser right, LpText p)
		{
			var leftResults = left.Do(p);
			foreach (var l in leftResults)
			{
				var r = right.Do(l.Rest);
				if (r.Success)
					yield return LpNode.Concat(l, r);
			}
		}

		/// <summary>
		/// Комбинатор последовательного применения парсеров.
		/// </summary>
		/// <param name="left">Левый парсер.</param>
		/// <param name="right">Правый парсер.</param>
		/// <returns>Результирующий парсер.</returns>
		public static LpmParser operator +(LpmParser left, LpsParser right)
		{
			return new LpmParser((p) => And(left, right, p));
		}

		/// <summary>
		/// Комбинатор последовательного применения парсеров.
		/// </summary>
		/// <param name="left">Левый парсер.</param>
		/// <param name="right">Правый парсер.</param>
		/// <returns>Результирующий парсер.</returns>
		public static LpmParser operator +(LpsParser left, LpmParser right)
		{
			return new LpmParser((p) =>
			{
				var prevResult = left.Do(p);
				if (!prevResult.Success)
					return new LpNode[0];
				return right.Do(prevResult.Rest).Select(next => LpNode.Concat(prevResult, next));
			});
		}

		/// <summary>
		/// Комбинатор последовательного применения парсеров.
		/// </summary>
		/// <param name="left">Левый парсер.</param>
		/// <param name="right">Правый парсер.</param>
		/// <returns>Результирующий парсер.</returns>
		public static LpmParser operator +(LpmParser left, LpmParser right)
		{
			return new LpmParser((p) => Lp.Next(left.Do(p), right).DistinctMatches()); // DistinctVoids()
		}

		#endregion And

		#region Or

		/// <summary>
		/// Комбинатор парсеров.
		/// Результирующий парсер объединяет результаты двух других парсеров, которые вызываются для одной и той же строки.
		/// </summary>
		/// <param name="p1">Парсер 1.</param>
		/// <param name="p2">Парсер 2.</param>
		/// <returns>Результирующий парсер.</returns>
		public static LpmParser operator |(LpmParser p1, LpmParser p2)
		{
			return new LpmParser((p) => p1.Do(p).Concat(p2.Do(p)).DistinctMatches()); // DistinctVoids()
		}



		#endregion Or
	}
}
